/*
 * noisetexture.cpp
 *
 *  Created on: 15/10/2012
 *      Author: cvan680
 */

#include "noisetexture.h"
#include "tgt/textureunit.h"
#include <vector>

#include <windows.h>
#include <math.h>
//using std::vector;
using tgt::TextureUnit;

namespace voreen {

/*
 * TODO:
 * - Do we need a RenderProcessor? Or maybe VolumeProcessor? Or VolumeRaycaster..?
 * - Multiple Volume in-ports (ideally a property to choose the number of inports so to variably create or remove them)
 */

NoiseTexture::NoiseTexture()
    : RenderProcessor()
    , outport_(Port::OUTPORT, "outport")
	, prop_xdim("dimension_x", "Dimension (x)", 512, 1, 2048)
	, prop_ydim("dimension_y", "Dimension (y)", 512, 1, 2048)
	, data_(new float[400*400])
{
    addPort(outport_);

    addProperty(prop_xdim);
    addProperty(prop_ydim);

    // create 400x400 noise texture
    float numElements = 400*400;
    memset(data_, 0, sizeof(float) * numElements);
    generateWhiteNoise(numElements);
}

Processor* NoiseTexture::create() const {
    return new NoiseTexture();
}

void NoiseTexture::process() {

	tgt::ivec2 dimensions = tgt::ivec2(prop_xdim.get(), prop_ydim.get());
	if(dimensions.x > 400){ generateSparseNoise(400*400); } // TODO get rid of this hack

	tgt::vec2 dims(400, 400);// = static_cast<tgt::vec2>(dimensions);
/*
    int numElements = dimensions.x * dimensions.y;
    // TODO make sure this doesnt cause any trouble with regards to mem allocation
    data_ = new float[numElements];
    memset(data_, 0, sizeof(float) * numElements);
    for (int i = 0; i < numElements; ++i){ data_[i] = rand() / static_cast<float>(RAND_MAX); }
*/
    noiseTexture_ = new tgt::Texture(reinterpret_cast<GLubyte*>(data_),
    		tgt::ivec3(dims, 1), GL_LUMINANCE, GL_LUMINANCE,
    		GL_FLOAT, tgt::Texture::NEAREST);

        GLuint texID = 0;
        if (noiseTexture_ != 0) {
        	noiseTexture_->uploadTexture();
            glBindTexture(GL_TEXTURE_2D, 0);
            //rebuildTexture_ = false;
            texID = noiseTexture_->getId();
        }


        // FIXME voreen crashes when viewportSize is smaller than texture size (400x400)
    	tgt::vec2 viewportSize = outport_.getSize();
        float scaleWidth = viewportSize.x / dims.x;
        float scaleHeight = viewportSize.y / dims.y;
        float scaling = (scaleWidth <= scaleHeight) ? scaleWidth : scaleHeight;
    	tgt::vec2 outputSize(dims * scaling);  // size of the quad on which the texture will be rendered
    	tgt::vec2 slicePos = (viewportSize - outputSize) / 2.0f;    // offset for the quad on which the texture will be rendered

        outport_.activateTarget("CcveProcessor::process()");

        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        glOrtho(0.0f, viewportSize.x, 0.0f, viewportSize.y, -1.0f, 1.0f);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glActiveTexture(GL_TEXTURE0);
        if (texID != 0) {
            // Bind the texture containing the flow image, if one has been generated
            //
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texID);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
        }

        glPushAttrib(GL_TEXTURE_BIT);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTranslatef(slicePos.x, slicePos.y, 0.0f);
        glBegin(GL_QUADS);
            glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
            glVertex2f(0.0f, 0.0f);

            glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 0.0f);
            glVertex2f(outputSize.x, 0.0f);

            glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 1.0f);
            glVertex2f(outputSize.x, outputSize.y);

            glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 1.0f);
            glVertex2f(0.0f, outputSize.y);
        glEnd();

        glPopAttrib();
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glDisable(GL_TEXTURE_2D);

        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();

        outport_.deactivateTarget();
}


void NoiseTexture::initialize() throw (VoreenException) {
    // call superclass function first
    RenderProcessor::initialize();
}

void NoiseTexture::deinitialize() throw (VoreenException) {
    RenderProcessor::deinitialize();
}

void NoiseTexture::generateWhiteNoise(int numElements){
    for (int i = 0; i < numElements; ++i){ data_[i] = rand() / static_cast<float>(RAND_MAX); }
}

void NoiseTexture::generateSparseNoise(int numElements){
    for (int i = 0; i < numElements; ++i){

    	data_[i] = ((rand() / static_cast<float>(RAND_MAX)) < 0.05) ? 1.0 : 0.0;
    }
}


}
